// =============================================================================
// | 包: jwts                                                                 |
// | 模块: JWT工具                                                           |
// | 功能: 提供JWT令牌的生成、解析和验证功能                                  |
// =============================================================================
package jwts

import (
	"blog_server/global"     // 全局变量包 - 提供配置信息访问
	"blog_server/models/enum" // 枚举包 - 提供用户角色枚举类型
	"errors"                 // 错误包 - 提供错误处理
	"fmt"                    // 格式化包 - 提供输出功能
	"strings"                // 字符串包 - 提供字符串操作
	"time"                   // 时间包 - 提供时间处理

	"github.com/dgrijalva/jwt-go" // JWT库 - 提供JWT实现
	"github.com/gin-gonic/gin"    // Web框架 - 提供HTTP上下文
)

// =============================================================================
// | 结构体: Claims                                                          |
// | 功能: 用户信息声明结构体                                                |
// | 用途: 存储JWT中包含的用户关键信息                                       |
// |----------------------------------------------------------------------------|
// | 字段: UserID   - uint          - 用户ID                                  |
// | 字段: Username - string        - 用户名                                  |
// | 字段: Role     - enum.RoleType - 用户角色 (枚举类型)                       |
// =============================================================================
type Claims struct {
	UserID   uint          `json:"userID"`   // 用户ID - JWT中存储的用户唯一标识
	Username string        `json:"username"` // 用户名 - JWT中存储的用户名称
	Role     enum.RoleType `json:"role"`     // 用户角色 - JWT中存储的用户权限角色
}

// =============================================================================
// | 结构体: MyClaims                                                         |
// | 功能: 自定义JWT声明结构体                                               |
// | 说明: 扩展标准JWT声明，包含自定义用户信息                               |
// |----------------------------------------------------------------------------|
// | 字段: Claims             - Claims            - 自定义用户信息            |
// | 字段: jwt.StandardClaims - 标准JWT声明结构体                             |
// =============================================================================
type MyClaims struct {
	Claims             // 嵌入自定义用户信息结构体
	jwt.StandardClaims // 嵌入JWT标准声明结构体
}

// =============================================================================
// | 函数: GetToken                                                           |
// | 参数: @claims - Claims (用户信息声明)                                     |
// | 返回: @token  - string (生成的JWT令牌)                                    |
// |       @error  - error  (生成失败时的错误信息)                             |
// | 功能: 生成JWT身份认证令牌                                                |
// | 说明: 根据用户信息和配置生成带有签名的JWT令牌                            |
// |----------------------------------------------------------------------------|
// | 流程:                                                                     |
// | 1. 创建自定义JWT声明，包含用户信息和标准声明                             |
// | 2. 设置过期时间和签发人（从全局配置读取）                                |
// | 3. 使用HS256算法创建JWT令牌                                              |
// | 4. 使用密钥对令牌进行签名并返回                                           |
// =============================================================================
func GetToken(claims Claims) (string, error) {
	// 创建包含用户信息和标准声明的自定义JWT声明
	cla := MyClaims{
		Claims: claims, // 嵌入用户信息
		StandardClaims: jwt.StandardClaims{
			// 设置过期时间 - 从配置文件读取小时数
			ExpiresAt: time.Now().Add(time.Duration(global.Config.Jwt.Expire) * time.Hour).Unix(),
			// 设置签发人 - 从配置文件读取
			Issuer: global.Config.Jwt.Issuer,
		},
	}
	// 使用HS256算法创建JWT令牌
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, cla)
	// 输出调试信息
	fmt.Println("Token = ", token)
	// 使用配置的密钥进行签名并返回令牌字符串
	return token.SignedString([]byte(global.Config.Jwt.Secret))
}

// =============================================================================
// | 函数: ParseToken                                                         |
// | 参数: @tokenString - string (待解析的JWT令牌)                             |
// | 返回: @claims - *MyClaims (解析后的声明对象)                              |
// |       @error  - error    (解析失败时的错误信息)                           |
// | 功能: 解析并验证JWT令牌                                                  |
// | 说明: 验证令牌的有效性并返回解析后的声明对象                             |
// |----------------------------------------------------------------------------|
// | 流程:                                                                     |
// | 1. 检查令牌是否为空                                                      |
// | 2. 使用密钥解析令牌并验证签名                                            |
// | 3. 处理各类错误情况（过期、无效、非法等）                                |
// | 4. 验证令牌有效性并返回解析后的声明                                      |
// =============================================================================
func ParseToken(tokenString string) (*MyClaims, error) {
	// 检查令牌是否为空
	if tokenString == "" {
		return nil, errors.New("请登录") // 返回空令牌错误
	}
	// 解析令牌并验证签名
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 返回用于验证的密钥
		return []byte(global.Config.Jwt.Secret), nil
	})
	// 处理解析错误
	if err != nil {
		// 根据错误类型返回对应的中文错误信息
		if strings.Contains(err.Error(), "token is expired") {
			return nil, errors.New("token过期")
		}
		if strings.Contains(err.Error(), "signature is invalid") {
			return nil, errors.New("token无效")
		}
		if strings.Contains(err.Error(), "token contains an invalid") {
			return nil, errors.New("token非法")
		}
		return nil, err // 返回其他错误
	}
	// 验证令牌有效性并返回声明
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		return claims, nil // 返回有效声明
	}
	return nil, errors.New("invalid token") // 返回令牌无效错误
}

// =============================================================================
// | 函数: ParseTokenByGin                                                    |
// | 参数: @c - *gin.Context (Gin上下文对象)                                  |
// | 返回: @claims - *MyClaims (解析后的声明对象)                              |
// |       @error  - error    (解析失败时的错误信息)                           |
// | 功能: 从Gin上下文中获取并解析JWT令牌                                     |
// | 说明: 优先从Header中获取token，若不存在则从Query参数中获取                |
// |----------------------------------------------------------------------------|
// | 流程:                                                                     |
// | 1. 优先从请求头获取token                                                 |
// | 2. 请求头不存在时从查询参数获取                                          |
// | 3. 调用ParseToken函数解析令牌                                            |
// =============================================================================
func ParseTokenByGin(c *gin.Context) (*MyClaims, error) {
	// 优先从请求头获取token
	token := c.GetHeader("token")
	// 如果请求头中没有token，则从查询参数获取
	if token == "" {
		token = c.Query("token")
	}
	// 调用ParseToken解析令牌
	return ParseToken(token)
}

// -----------------------------------------------------------------------------| 
// | 历史版本JWT实现代码（已注释）                                               |
// | 说明：以下为系统早期使用的JWT实现，当前已被上方的优化版本替代               |
// -----------------------------------------------------------------------------| 

// Token过期时间设置为2小时
// const TokenExpireDuration = time.Hour * 2
// const TokenExpireDuration = time.Second * 60

// // JWT签名密钥
// var Secret = []byte("人生路漫漫")

// // 自定义Claims结构体
// type MyClaims struct {
// 	UserName string
// 	jwt.StandardClaims
// }

// // 生成Token
// func GetToken(username string) (string, error) {
// 	cla := MyClaims{
// 		UserName: username,
// 		StandardClaims: jwt.StandardClaims{
// 			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
// 			Issuer:    "lx-jwt",                                   // 签发人                                      // 签发人 yaml 读取
// 		},
// 	}
// 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, cla)
// 	fmt.Println("Token = ", token)
// 	return token.SignedString(Secret) // 进行签名生成对应的token
// }

// // 解析Token
// func ParseToken(tokenString string) (*MyClaims, error) {
// 	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
// 		// return Secret, nil
// 	})
// 	if err != nil {
// 		return nil, err
// 	}
// 	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
// 		return claims, nil
// 	}
// 	return nil, errors.New("invalid token")
// }
